/**
 * @param {Object} str
 * 判断车牌号是否正确
 */
export function isCarLicense(str) {
	if (!str) {
		return false
	}
	return /^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))$/.test(str);
}

/**
 * 时间格式化
 * @param {*} date
 * @param {*} fmt 'yyyy-MM-dd HH:mm:ss'
 */
export function format(date, fmt) {
	const o = {
		'M+': date.getMonth() + 1, // 月份
		'd+': date.getDate(), // 日
		'H+': date.getHours(), // 小时
		'm+': date.getMinutes(), // 分
		's+': date.getSeconds(), // 秒
		'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
		S: date.getMilliseconds() // 毫秒
	}
	if (/(y+)/.test(fmt)) {
		fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
	}
	for (let k in o) {
		if (new RegExp('(' + k + ')').test(fmt)) {
			fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length))
		}
	}
	return fmt
}

/**
 * 比较两个字符串是否含有共同的子字符串
 * @param str1 字符串一
 * @param str2 字符串二
 * @param includeSelf 子字符串是否包括本身
 * @return 比较结果
 */
export function hasSameSubStr(str1, str2, includeSelf = false)
{
	if (!str1.length || !str2.length) return false
	let shortStr = str1.length > str2.length ? str2: str1;
	let longStr = str1.length > str2.length? str1: str2;
	let temp = "";
	
	for(let i = 0; i <= shortStr.length - 2; i++)
	{
		for(let j = i + 2; j <= shortStr.length; j++)
		{
			temp = shortStr.substring(i, j);
			let flag1 = includeSelf && longStr.indexOf(temp) >= 0;
			let flag2 = !includeSelf && temp !== shortStr && longStr.indexOf(temp) >= 0;
			
			if(flag1 || flag2)
			{
				return true;
			}
		}
	}
	return false;
}

/**
 * 方法一
 * 对象数组根据具体参数去重
 * @param {*} arr 数组
 * @param {*} attr 要去重的属性
 */
export function repeatArr (arr, attr) {
	const map = new Map()
	for (let i of arr) {
		if (!map.has(i[attr ])) {
			map.set(i[attr ], i)
		}
	}
	return [...map.values()]
}

/**
 * 方法二
 * 对象数组根据具体参数去重
 * @param {*} arr 数组
 * @param {*} attr 要去重的属性
 */
export function repeatArr (arr, attr) {
	const map = new Map()
	return arr.filter(item => {
        const attrVal = item[attr]
        return !map.has(attrVal) && map.set(attrVal, 1)
    })
}

/**
 * 防抖函数（常用于input框搜索情况）
 * @param {*} func 
 * @param {*} delay 
 * @param {*} immediate 
 * @returns 
 */
export function debounce(func, delay, immediate = true) {
  let timer = null
  return function(args) {
    let _this = this
    if (timer) {
      clearTimeout(timer)
    }
    if (immediate) {
      let now = !timer
      timer = setTimeout(() => {
        timer = null
      }, delay)
      now && func.call(_this, args)
    } else {
      timer = setTimeout(() => {
        timer = null
        func.call(_this, args)
      }, delay)
    }
  }
 }

/**
 * 节流函数（常用于onresize, onmouseover情况）
 * @param {*} func 
 * @param {*} delay 
 * @param {*} immediate 
 * @returns 
 */
export function throttle(func, delay, immediate = true) {
  let timer = null
  return function (args) {
    let _this = this
    if (!timer) {
      if (immediate) {
        func.call(_this, args)
        timer = setTimeout(() => {
          timer = null
        }, delay)
      } else {
        timer = setTimeout(() => {
          func.call(_this, args)
          timer = null
        }, delay)
      }
    }
  }
}

/**
 * url中将参数部分转为对象,处理的数据必须要有 ？分割
 * @param {string} url
 * @returns {Object}
 */
export function param2Obj(url) {
	let search = ''
	if (url.indexOf('?') !== -1) {
		search = url.split('?')[1]
	}
	if (!search) return {}

	return JSON.parse(
		'{"' +
			decodeURIComponent(search)
				.replace(/"/g, '\\"')
				.replace(/&/g, '","')
				.replace(/=/g, '":"')
				.replace(/\+/g, ' ') +
			'"}'
	)
}

/**
 * @param {any} 要加密或解密的数据
 * @returns any.base64 
 */
class Base64 {
    _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
    encode(e) {
      let t = "";
      let n, r, i, s, o, u, a;
      let f = 0;
      e = this._utf8_encode(e);
      while (f < e.length) {
        n = e.charCodeAt(f++); 
        r = e.charCodeAt(f++);
        i = e.charCodeAt(f++);
        s = n >> 2;
        o = (n & 3) << 4 | r >> 4;
        u = (r & 15) << 2 | i >> 6;
        a = i & 63;
        if (isNaN(r)) {
            u = a = 64
        } else if (isNaN(i)) {
            a = 64
        }
        t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a)
      }
      return t
    }
    decode(e) {
      let t = "";
      let n, r, i;
      let s, o, u, a;
      let f = 0;
      e=e.replace(/[^A-Za-z0-9+/=]/g,"");
      while (f < e.length) {
          s = this._keyStr.indexOf(e.charAt(f++));
          o = this._keyStr.indexOf(e.charAt(f++));
          u = this._keyStr.indexOf(e.charAt(f++));
          a = this._keyStr.indexOf(e.charAt(f++));
          n = s << 2 | o >> 4;
          r = (o & 15) << 4 | u >> 2;
          i = (u & 3) << 6 | a;
          t = t + String.fromCharCode(n);
          if (u != 64) {
              t = t + String.fromCharCode(r)
          }
          if (a != 64) {
              t = t + String.fromCharCode(i)
          }
      }
      t = this._utf8_decode(t);
      return t
    }
    _utf8_encode(e) {
      e = e.replace(/rn/g, "n");
      let t = "";
      for (let n = 0; n < e.length; n++) {
        let r = e.charCodeAt(n);
        if (r < 128) {
            t += String.fromCharCode(r)
        } else if (r > 127 && r < 2048) {
            t += String.fromCharCode(r >> 6 | 192);
            t += String.fromCharCode(r & 63 | 128)
        } else {
            t += String.fromCharCode(r >> 12 | 224);
            t += String.fromCharCode(r >> 6 & 63 | 128);
            t += String.fromCharCode(r & 63 | 128)
        }
      }
      return t
    }
    _utf8_decode(e) {
      let t = "";
      let n = 0;
      let r = 0;
      let c1 = 0;
      let c2 = 0;
      while (n < e.length) {
          r = e.charCodeAt(n);
          if (r < 128) {
              t += String.fromCharCode(r);
              n++
          } else if (r > 191 && r < 224) {
              c2 = e.charCodeAt(n + 1);
              t += String.fromCharCode((r & 31) << 6 | c2 & 63);
              n += 2
          } else {
              c2 = e.charCodeAt(n + 1);
              c3 = e.charCodeAt(n + 2);
              t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
              n += 3
          }
      }
      return t
  }
}

export default Base64

// 调用方式
new Base64().encode('xxx')

/**
 * 定时器类
 */
function Timer() {
	this._id = null // 该类唯一id
	this.timeId = null // 该类唯一定时id
	this.func = null // 执行用户自定义回调函数
}

/**
 * 递归定时器
 * @param {Function} func 用户自定义回调函数
 * @param {Number} interval 延时时间
 * @param {Boolean} flag true => setTimeout, false => setInterval, 默认为 true
 * @param {Boolean} immediate 是否立即执行，默认为 false
 * @returns
 */
Timer.prototype.repeat = function(func, interval, flag = true, immediate = false) {
	// 实例无回调函数 则初始化回调函数
	if (this.func === null) {
		this.func = func
	}

	// 立即执行函数
	if (immediate) {
		func()
	}

	this.timeId = setTimeout(() => {
		func()
		if (!flag) {
			// 确保repeat中只立即执行一次用户自定义回调函数
			this.repeat(func, interval, flag)
		}
	}, interval)
}

// 清除定时器
Timer.prototype.clear = function() {
	if (this.timeId) {
		console.log('clear timer', this.timeId)
		clearTimeout(this.timeId)
		this.timeId = null
	} else {
		console.log('目前定时器为null ')
	}
}

export default Timer

// // 调用方式
// this.timer = new Timer()
// this.timer.repeat(
// 		() => {
//             // 要执行的逻辑
// 			},
// 		1000,
// 		false,
// 		false
// 	)

/**
 * 身份证号验证
 */
export const checkIdCard = (value, callback) => {
  if (!value) {
    return callback(new Error('身份证号不能为空'))
  }
  else {
    let reg = /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/
    if(reg.test(value)){
      callback();
    }
    else{
      return callback(new Error('身份证号格式不正确'))
    }
  }
}

/**
 * 手机号验证
 */
export const checkPhoneNumber = (value, callback) => {
  if (!value) {
    return callback(new Error('手机号不能为空'))
  }
  else {
    let reg = /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/
    if(reg.test(value)){
      callback();
    }
    else{
      return callback(new Error('手机号格式不正确'))
    }
  }
}

/**
 * 阿拉伯数字转为汉字
 */
export function formatNumber(number) {
	const chnNumChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
	const chnUnitSection = ['', '万', '亿', '万亿', '亿亿']
	const chnUnitChar = ['', '十', '百', '千']

	// 节内转换算法
	function SectionToChinese(section) {
		let strIns = ''
		let chnStr = ''
		let unitPos = 0
		let zero = true
		while (section > 0) {
			const v = section % 10
			if (v === 0) {
				if (!zero) {
					zero = true
					chnStr = chnNumChar[v] + chnStr
				}
			} else {
				zero = false
				strIns = chnNumChar[v]
				strIns += chnUnitChar[unitPos]
				chnStr = strIns + chnStr
			}
			unitPos++
			section = Math.floor(section / 10)
		}
		return chnStr
	}
	// 转换算法主函数
	function NumberToChinese(num) {
		let unitPos = 0
		let strIns = ''
		let chnStr = ''
		let needZero = false

		if (num === 0) {
			return chnNumChar[0]
		}
		while (num > 0) {
			const section = num % 10000
			if (needZero) {
				chnStr = chnNumChar[0] + chnStr
			}
			strIns = SectionToChinese(section)
			strIns += section !== 0 ? chnUnitSection[unitPos] : chnUnitSection[0]
			chnStr = strIns + chnStr
			needZero = section < 1000 && section > 0
			num = Math.floor(num / 10000)
			unitPos++
		}
		return chnStr
	}
	return NumberToChinese(number)
}

/**
 * 深拷贝
 */
export const deepClone = obj => {
	const _toString = Object.prototype.toString

	// null, undefined, non-object, function
	if (!obj || typeof obj !== 'object') {
		return obj
	}

	// DOM Node
	if (obj.nodeType && 'cloneNode' in obj) {
		return obj.cloneNode(true)
	}

	// Date
	if (_toString.call(obj) === '[object Date]') {
		return new Date(obj.getTime())
	}

	// RegExp
	if (_toString.call(obj) === '[object RegExp]') {
		const flags = []
		if (obj.global) {
			flags.push('g')
		}
		if (obj.multiline) {
			flags.push('m')
		}
		if (obj.ignoreCase) {
			flags.push('i')
		}

		return new RegExp(obj.source, flags.join(''))
	}

	const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}

	for (const key in obj) {
		result[key] = deepClone(obj[key])
	}

	return result
}

/**
 * before => 差异前数组 type: Array<{id: number, name: string, pid: number, nodeType: number, children: Array[]}>
 * after => 差异后数组 type: Array<{id: number, name: string, pid: number, nodeType: number, children: Array[]}>
 * type => 用于标识两对象不同的地方， 默认为id
 */
export function compareArray(before, after, type = 'id') {
  const res = {
    add: [], // 新增
    remove: [], // 删除
    update: [] // 更新
  }
  const compareMap = new Map()
  for (let i = 0; i < before.length; i++) {
    compareMap.set(before[i][type], before[i])
    if (before[i].children && before[i].children.length > 0) {
      compareMap.get(before[i][type]).children = before[i].children
    }
  }
  // 差异比较 compareMap中保存的是before有的而after不存在的
  for (let i = 0; i < after.length; i++) {
    if (!compareMap.has(after[i][type])) {
      res.add.push(after[i])
    } else {
      const beforeChild = compareMap.get(after[i][type]).children
      if (beforeChild.length) {
      const afterChild = after[i].children
      const resVal = {
        id: after[i].id,
        name: after[i].name,
        nodeType: after[i].nodeType,
        children: []
      }
      for (let j = 0; j < beforeChild.length; j++) {
        if (!afterChild[j].checked) {
          resVal.children.push({
            checked: afterChild[j].checked,
            id: afterChild[j].id,
            name: afterChild[j].name,
            pid: afterChild[j].pid
          })
        }
      }
      res.update.push(resVal)
      }
      compareMap.delete(after[i][type])
    }
  }
  compareMap.forEach((value, key, map) => {
    res.remove.push(value)
  })
  return res
}

import Vue from 'vue'

/**
 * 全局事件bus ，每一次事件监听后需要在beforeDestroy中取消监听 this.$notifiy.off(type)，否则会重复注册，造成不可预知的错误
 */
class Notify {
	static eventBus = new Vue()

	static events = []

	static send(type, data) {
		Notify.eventBus.$emit(type, data)
	}

	static on(type, callback) {
		if (Notify.events.includes(type)) {
			return
		}
		Notify.events.push(type)
		Notify.eventBus.$on(type, res => {
			callback(res ? res : null)
		})
	}

	static off(type) {
		if (Notify.events.includes(type)) {
			Notify.events = Notify.events.filter(i => i !== type)
			Notify.eventBus.$off(type)
		} else {
			console.warn(`EventBus不存在type${type}`)
		}
	}
}

export default Notify

// 调用方式
this.$notify.on('xxx', () => {})
this.$notify.off('xxx')

/**
 * 实现打开、退出全屏
 */
export const FullScreen (el) {
	/* eslint-disable */
	const isFullscreen = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen
	if (!isFullscreen) {
		// 进入全屏,多重短路表达式
		;(el.requestFullscreen && el.requestFullscreen()) ||
			(el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
			(el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
			(el.msRequestFullscreen && el.msRequestFullscreen())
	} else {
		// 退出全屏,三目运算符
		document.exitFullscreen
			? document.exitFullscreen()
			: document.mozCancelFullScreen
			? document.mozCancelFullScreen()
			: document.webkitExitFullscreen
			? document.webkitExitFullscreen()
			: ''
	}
}

/**
 * 验证密码 必须包含数字、大小写字母、特殊符号，密码长度在 10 到 20 个字符
 */
export const validatePwd (value, callback) {
	if (!value) {
		return callback(new Error('密码不能为空'))
	} else if (value.length < 10 || value.length > 20) {
		return callback(new Error('密码长度应在 10 到 20 个字符'))
	} else {
		const reg = /(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[^a-zA-Z0-9]).{10,20}/
		if (reg.test(value)) {
			callback()
		} else {
			return callback(new Error('密码必须包含大小写字母、数字、特殊字符4种'))
		}
	}
}

/**
 * async/await 错误异常抛出
 */
export function to(promise: Promise<any>) {
  return promise.then((data: any) => [null, data]).catch((err: any) => [err])
}

// 调用方式
let [result_err, result] = await to(getApi())

/**
 * 数组转化为树形结构（常用于后台菜单数组转换成树形结构）
 */
export function arrToTree(data) {
  let result = []
  let map = new Map()
  data.forEach(item => {
    // 存入字典集里
    map.set(item.id, item)
  })
  data.forEach(item => {
    // 判断字典集里是否有该键
    let parent = map.get(item.pid)
    if (parent) {
      // ?. 可选链操作符，常用于判断对象里是否有该属性
      // ?? 合并操作符 或等于||，但是不会对左边值进行处理
      (parent?.children??(parent.children = [])).push(item)
    } else {
      result.push(item)
    }
  })
  return result
}

/**
 * 数字取整数部分
 * @param {*} v 
 */
export function numberTrunc (v) {
	if (Math.trunc) {
		return Math.trunc(v)
	} else {
		v = +v
		if (!isFinite(v)) return v;
		return (v - v % 1) || (v < 0 ? -0 : v === 0 ? v : 0);
	}
}

/**
 * 网址url验证
 */
export function checkUrl (value) {
	if (!value) {
		  return false
	}
	else {
	  let reg = /^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?$/
	  if(reg.test(value)){
	    return true
	  }
	  else{
	    return false
	  }
	}
}

/**
 * 驼峰转下划线写法
 * @param str 
 */
export function humpToUnderline(str: String) {
  return str.replace(/([A-Z])/g, '_$1')
}

/**
 * 下划线转驼峰写法
 * @param str 
 * @returns 
 */
export function undelineToHump(str: String) {
  for (let i of str.match(/_(.)/g)) {
    str = str.replace(i, i.replace('_', '').toUpperCase())
  }
  return str
}

/**
 *  宽高为x，y的矩形框分别有m个正方形，n个长方形
 * 注: 本长方形不包括正方形
 * @param {*} x 
 * @param {*} y 
 * @returns 
 */
export const rectangleAlgo = (x, y) => {
  const square = (x, y) => {
    if (x === 1 || y === 1) {
      return x * y
    } else {
      return x * y + square(x - 1, y - 1)
    }
  }
  const rectangle = a => {
    if (a === 1) {
      return 1
    } else {
      return a + rectangle(a - 1)
    }
  }
  const squareNum = square(x, y) // 正方形个数
  const rectangleNum = rectangle(x) * rectangle(y) - squareNum // 长方形个数
  return [squareNum, rectangleNum]
}


/**
 * 判断当前变量的类型 
 * @param {*} obj 
 * @returns 
 */
export const getDataType = (obj) => {
  const str = Object.prototype.toString.call(obj); // 检测基本类型值，引用类型值的类型
  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  };
  if(obj instanceof Element){
    return 'element';
  }
  return map[str];
}

// 举例：

// console.log(getDataType('hello lindadayo'))
// 输出： 'string'


import { customRef } from 'vue'
/**
 * Ref防抖
 * @param value v-model， 可初始为空字符
 * @param delay 延迟时间
 * @returns
 */
export function debounceRef(value, delay = 500) {
  let timer;
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(val) {
        clearTimeout(timer)
        timer = setTimeout(() => {
          value = val
          trigger()
        }, delay)
      }
    }
  })
}

//  举例：

// import { debounceRef } from 'xxx'
// setup() {
//      const name= debounceRef('', 500) // 输入值时延时500ms
//      return () => (
//         <input v-model={name.value} />
//     )
// }

/**
 * 随机标识符生成
 * @param {*} length 标识符长度，长度越长越唯一，但是过长循环时间会增加，默认为16位，基本合适
 */
export function randomKey(length: number = 16): string {
  const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_=-'
  let result = ''
  for (let i = length; i > 0; --i) {
    result += chars[Math.floor(Math.random() * chars.length)]
  }
  return result
}

/**
 * 文件大小单位转换
 * @param size
 * @returns
 */
export function getfilesize(size) {
  if (!size) {
    return '';
  }
  const num = 1024.00;
  if (size < num) {
    return size + 'B';
  } else if (size < Math.pow(num, 2)) {
    return (size / num).toFixed(2) + 'K'; // kb
  } else if (size < Math.pow(num, 3)) {
    return (size / Math.pow(num, 2)).toFixed(2) + 'M'; // M
  } else if (size < Math.pow(num, 4)) {
    return (size / Math.pow(num, 3)).toFixed(2) + 'G'; // G
  } else {
    return (size / Math.pow(num, 4)).toFixed(2) + 'T'; // T
  }
}
